#!/usr/bin/python3
#-*-coding:utf-8-*-

###############################################################
## Name       : auto_unfold.py
## Author     : gaojiaming
## Date       : Tue Jan 10 17:28:59 CST 2023
## Description:
##
##
###############################################################

import os
import sys
import re
import argparse
 
def input_args_proc():#{{{
    parser = argparse.ArgumentParser(description="argparse info")
    parser.add_argument('-o', action='store_true', default=False, help='open this script')
    parser.add_argument('-d', action='store_true', default=False, help='del auto generate')
    parser.add_argument('-f', help='input name')
    result = parser.parse_args()
    if result.o == True:
        os.system("gvim %s" % __file__)
        sys.exit(0)
    del_gen = 0
    if result.d == True:
        del_gen = 1
    if not result.f:
        raise ValueError("No input file")
    inp_file = result.f
    return inp_file, del_gen
pass#}}}

def if_param_rep(match):
    matched_string = match.group(0)
    if matched_string in lp_rp_dict:
        processed_string = lp_rp_dict[matched_string]
    else:
        processed_string = matched_string
    return processed_string

#{{{gen_unfold_main
#para_list = ['i', 'j'] 
#loop_list = [3, 3] 
#gen_list  = ['wire [15:0] sig#j#_sub#i#;', 'wire [15:0] aaa#j#_bbb#i#;']
def gen_unfold_main(para_list, loop_list, gen_list):
    return_list = []
    for line in gen_list:
        re_if = re.search(r"IF\(#(.*?)#\)", line)
        if_power = 0
        if_str = ""
        if(re_if):
            if_power = 1
            if_str = re_if.group(1)
            line = re.sub(r"IF\(#(.*?)#\)", "", line)
            i = 0
            for para in para_list:
                if_str = re.sub(para, str(loop_list[i]), if_str)
                i += 1
            if_str = re.sub(r"(\w+)", if_param_rep, if_str) #IF(#i==LOOP_I_MAX#) -> IF(#i==5#)
            if eval(if_str) == False:
                continue

        while re.search(r"(.*)#(.*?)#(.*)", line):
            res = re.search(r"(.*)#(.*?)#(.*)", line)
            bf = res.group(1)
            rp = res.group(2)
            af = res.group(3)
            i = 0
            for para in para_list:    
                #print(para, loop_list[i])
                rp = re.sub(para, str(loop_list[i]), rp)
                #print(rp)
                i += 1
            if not re.search(r"^\d+$", rp):
                rp = str(eval(rp))
            line = bf + rp + af
        return_list.append(line)
    return return_list
pass#}}}

#{{{gen_unfold
#para_list = ['i', 'j'] 
#min_list  = [3, 3] 
#max_list  = [1, 4] 
#gen_list  = ['wire [15:0] sig#j#_sub#i#;', 'wire [15:0] aaa#j#_bbb#i#;']
#need gen loop_list = [3, 3]  <-> [x, y]
#                     [2, 3]  <-> [x, y]
#                     [1, 3]  <-> [x, y]
#                     [3, 4]  <-> [x, y]
#                     [2, 4]  <-> [x, y]
#                     [1, 4]  <-> [x, y]
#                     [3, 5] is break time!
def gen_unfold(para_list, min_list, max_list, gen_list):
    upfold_list = []

    loop_list = min_list.copy()
    
    zero_en  = 0
    zero_pos = 0

    #max_flag = 1
    #if max_list[-1] < min_list[-1]:
    #    max_flag = 0
        
    while True:
        if max_list[-1] >= min_list[-1] and loop_list[-1] > max_list[-1]:
            break
        if max_list[-1] < min_list[-1] and loop_list[-1] < max_list[-1]:
            break
        
        upfold_list.extend(gen_unfold_main(para_list, loop_list, gen_list))
        #print(para_list, loop_list, gen_list, zero_pos)
        #print("gen_unfold para_list, loop_list, gen_list, zero_pos")
        
        #for i 1..0
        #for j 3..2
        #for k 4..5
        #['i', 'j', 'k'] [1, 3, 4] ['wire [15:0] #k#_sig#j#_sub#i#;'] 0, i will be change, i for loc0
        #['i', 'j', 'k'] [0, 3, 4] ['wire [15:0] #k#_sig#j#_sub#i#;'] 1, j will be change, j for loc1
        #['i', 'j', 'k'] [1, 2, 4] ['wire [15:0] #k#_sig#j#_sub#i#;'] 0, i will be change, i for loc0
        #['i', 'j', 'k'] [0, 2, 4] ['wire [15:0] #k#_sig#j#_sub#i#;'] 2, k will be change, k for loc0
        #j will be change, i need to change to min
        #k will be change, i j need to change to min

        if zero_en != 0:
            for i in range(0, zero_pos):
                loop_list[i] = min_list[i]
            
            if min_list[zero_pos] <= max_list[zero_pos]:
                max_flag = 1
            else:
                max_flag = 0

            loop_list[zero_pos] = loop_list[zero_pos]+1 if max_flag == 1 else loop_list[zero_pos]-1
            zero_en  = 0
            zero_pos = 0
        else:
            if min_list[zero_pos] <= max_list[zero_pos]:
                max_flag = 1
            else:
                max_flag = 0

            loop_list[0] = loop_list[zero_pos]+1 if max_flag == 1 else loop_list[zero_pos]-1
        
        #print("loop_list: {0}".format(loop_list))
        for loop in range(1, len(loop_list)): #i j k next value [3 2 2]
            # loop is 1, 2, 3 for [i, j, k]
            loc_zero_pos = loop
            loc_zero_en  = 1
            
            for sub in range(0, loop):
                if min_list[sub] <= max_list[sub]:
                    max_flag = 1
                else:
                    max_flag = 0
                    
                if max_flag == 1 and max_list[sub] - loop_list[sub] >= 1:
                    loc_zero_en = 0
                    break
                if max_flag == 0 and loop_list[sub] - max_list[sub] >= 1:
                    loc_zero_en = 0
                    break
            
            if loc_zero_en == 0:
                break
            else:
                zero_en  = loc_zero_en
                zero_pos = loc_zero_pos
    return upfold_list
pass#}}}

def sys_unfold(inp_file):#{{{
    para_list   = []
    min_list    = []
    max_list    = []
    gen_list    = []
    upfold_list = []
    return_list = []

    handle = del_gen_code(inp_file)
    auto_unfold_power = 0
    for line in handle:
        return_list.append(line.rstrip())
        res_s = re.match(r"/\*\s*AUTO_UNFOLD", line) #Æ¥Åä /* AUTO_UNFOLD
        res_e = re.match(r"\s*END\s*\*/", line) #Æ¥Åä END */
        #res_f = re.match(r"\#for\s+(\w+)\s+(\d+)\.\.(\d+)", line) #Æ¥Åä #for i 0..6
        res_f = re.match(r"\#for\s+(\w+)\s+(\w+)\.\.(\w+)", line) #Æ¥Åä #for i 0..6
        #if res_f:
        #  print(line, auto_unfold_power)
        if res_s:
            auto_unfold_power = 1
        elif res_e:
            auto_unfold_power = 0
            upfold_list.append("//AUTO_UNFOLD_START")
            upfold_list.extend(gen_unfold(para_list, min_list, max_list, gen_list))
            upfold_list.append("//AUTO_UNFOLD_END")
            return_list.extend(upfold_list)
            upfold_list = []
            para_list   = []
            min_list    = []
            max_list    = []
            gen_list    = []
        elif auto_unfold_power == 1 and res_f:
            loop  = res_f.group(1)
            start = res_f.group(2)
            end   = res_f.group(3)

            if start in lp_rp_dict:
                start = lp_rp_dict[start]
            if end in lp_rp_dict:
                end = lp_rp_dict[end]

            #print(res_f.group(1), int(res_f.group(2)), int(res_f.group(3)))
            #para_list.append(res_f.group(1))
            #min_list.append(int(res_f.group(2)))
            #max_list.append(int(res_f.group(3)))
            para_list.append(loop)
            min_list.append(int(start))
            max_list.append(int(end))
        elif auto_unfold_power == 1:
            gen_list.append(line.rstrip())   

    return return_list
pass#}}}

def del_gen_code(inp_file):#{{{
    tmp_list = []
    shot_en = 0
    with open(inp_file, "r") as hd:
        handle = hd.readlines()
        for line in handle:
            line = line.rstrip()
            if re.search(r"//AUTO_UNFOLD_START", line):
                shot_en = 1
            if shot_en == 0:
                tmp_list.append(line)
            if re.search(r"//AUTO_UNFOLD_END", line):
                shot_en = 0
    return tmp_list
pass#}}}

def del_note_code(handle):#{{{
    ret_handle = []
    del_flag = 0
    for line in handle:
        re_s  = re.search(r"^\/\*", line)
        re_e  = re.search(r"\*\/", line)
        re_se = re.search(r"^\/\*.*\*\/$", line)
        re_se_range = re.search(r".+\/\*.*\*\/$", line)
        line  = re.sub(r"\/\/.*$", "", line)
        if re_se:
            continue
        elif re_se_range:
            line = re.sub(r"\/\*.*\*\/", "", line)
        elif re_s:
            del_flag = 1
        elif re_e:
            del_flag = 0
            continue

        if del_flag != 1:
            ret_handle.append(line)

    return ret_handle
#}}}

def localparam_rep(inp_file):#{{{
  global lp_rp_dict
  lp_rp_dict = {}
  handle = del_gen_code(inp_file)
  handle = del_note_code(handle)
  for line in handle:
    re_lp = re.match(r"\s*localparam\s+(.*);", line)
    if re_lp:
      for para in re_lp.group(1).split(","):
        para = para.strip()
        (p, v) = para.split("=")
        p = p.strip()
        v = v.strip()
        lp_rp_dict[p] = v
#}}}

def main():
    return_list = []
    inp_file, del_gen = input_args_proc()
    localparam_rep(inp_file)
    if del_gen == 1:
        return_list = del_gen_code(inp_file)
    else:
        return_list = sys_unfold(inp_file)
    for line in return_list:
        print(line)
        pass
pass
 
if __name__ == "__main__":
    main()
